package com.koylu.faces.onload;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.digester.Digester;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class Onload implements PhaseListener{
	
	private static String  config     = "onload";
	private static Log     logger     = null;
	private static Map     onloadMap  = new HashMap();
	private static List    onloadList = null;
	
	public Onload() {
		logger = LogFactory.getLog(getClass());
	}
	
	public void afterPhase(PhaseEvent event) {}
	
	public PhaseId getPhaseId() {
		return PhaseId.ANY_PHASE;
	}	

	public void beforePhase(PhaseEvent event) {
		if( onloadList == null){			
			initialize(event.getFacesContext());			
		}
		String sessionId = ((HttpServletRequest)event.getFacesContext().getExternalContext().getRequest()).getSession().getId();
		if(event.getPhaseId() == PhaseId.PROCESS_VALIDATIONS){
			onloadMap.put(sessionId,new Boolean("true"));
		}else if(event.getPhaseId() == PhaseId.RENDER_RESPONSE){
			if(!((Boolean)onloadMap.get(sessionId)).booleanValue()){
				FacesContext facesContext = event.getFacesContext(); 
				String view = facesContext.getViewRoot().getViewId();
				logger.info("view-->"+view);
				Rule rule = findRule(view);
				logger.info("rule-->"+rule);
				processRule(rule, facesContext);
			}
			onloadMap.remove(sessionId);
		}else{
			onloadMap.put(sessionId,new Boolean("false"));
		}
	}
	
	/*private void processRule(Rule rule,FacesContext ctx){
		if(rule == null){
			return;
		}
		rule.processRule(ctx);
	}*/
	
	private void processRule(Rule rule,FacesContext ctx){
		if(rule == null){
			return;
		}
		processRule(findDependsRule(rule.getDepends()),ctx);
		rule.processRule(ctx);
	}	
	
	private Rule findRule(String view){
		if(view == null){
			return null;
		}
		Rule rule = (Rule)onloadMap.get(view);
		if(rule != null){			
			return rule;
		}
		for(int k=0;k<onloadList.size();k++){
			rule = (Rule)onloadList.get(k);
			if(rule.check(view)){
				onloadMap.put(view,rule);				
				return rule;
			}
		}		
		return null;		
	}
	
	private Rule findDependsRule(String view){
		if(view == null){
			return null;
		}
		Rule rule = (Rule)onloadMap.get(view);
		if(rule != null){			
			return rule;
		}
		for(int k=0;k<onloadList.size();k++){
			rule = (Rule)onloadList.get(k);
			if(view.equals(rule.getView())){
				onloadMap.put(view,rule);
				return rule;
			}
		}		
		return null;	
	}	
	
	private synchronized void initialize(FacesContext ctx){
		try {			
			String url = ctx.getExternalContext().getInitParameter(config);
			InputStream configStream = FacesContext.getCurrentInstance().getExternalContext().getResourceAsStream(url);			
			Digester digester = new Digester();
			digester.addObjectCreate("onload",ArrayList.class);
			digester.addObjectCreate("onload/rule",Rule.class);
			digester.addSetProperties("onload/rule","depends","depends");
			digester.addSetNext("onload/rule","add");
			digester.addBeanPropertySetter("onload/rule/view");
			digester.addBeanPropertySetter("onload/rule/method");			
			digester.addCallMethod("onload/rule/action", "setAction", 2);
			digester.addCallParam("onload/rule/action", 0, "navigate");
			digester.addCallParam("onload/rule/action", 1);
			digester.addBeanPropertySetter("onload/rule/bsh");			
			onloadList = (List)digester.parse(configStream);
			logger.info("ONLOAD INITIALIZED-->"+onloadList);
		} catch (Exception e) {
			onloadList = new ArrayList();
			logger.error(e);
		}finally{
			Collections.sort(onloadList,new RuleComparator());
		}
	}
	
	class RuleComparator implements Comparator{
		public int compare(Object arg0, Object arg1) {
			int result = -1;			
			int len0 = ((Rule)arg0).getView()==null?0:((Rule)arg0).getView().length();
			int len1 = ((Rule)arg1).getView()==null?0:((Rule)arg1).getView().length();
			if( len0 < len1 ){
				result = 1;
			}else if( len0 == len1 ){
				result = 0 ;
			}
			return result;
		}	
	}
}
